
#ifndef __COMMON_H
#define __COMMON_H

#include "wtype.h"
#include <stdint.h>
#include <random>
#include <chrono>

#define SIMULATE_MACRO (1)

#define V_FLAG_INTERRUPT (4)   // 运算完成产生完成标志和中断
#define V_FLAG_INTERRUPT_0 (0) // 运行中断号0
#define V_FLAG_INTERRUPT_1 (1)
#define V_FLAG_INTERRUPT_2 (2)
#define V_FLAG_INTERRUPT_3 (3)

#define V_AVG_MODE (16)   // 平均值
#define V_SUM_MODE (8)    // 和
#define V_MIN_MODE (4)    // 最小值
#define V_MAX_MODE (2)    // 最大值
#define V_NORMAL_MODE (1) // 正常模式

#define V_SAVE_NORMAL_INDEX (0) // 正常模式
#define V_SAVE_SUM_INDEX (1)    // 和
#define V_SAVE_MAX_INDEX (2)    // 最大值
#define V_SAVE_MIN_INDEX (3)    // 最小值
#define V_SAVE_AVG_INDEX (4)    // 平均值

#define V_IN_TYPE_64 (3) // 输入数据类型64bit
#define V_IN_TYPE_32 (2)
#define V_IN_TYPE_16 (1)
#define V_OUT_TYPE_32 (1) // 输出数据类型32bit
#define V_OUT_TYPE_16 (0)

#define V_IN_MODE_FIXED (0) // 固定数据
#define V_IN_MODE_ARRAY (1) // 数组

#define V_IN2_ENABLE (1)  // 使能输入2
#define V_IN2_DISABLE (0) // 去使能输入2

#define V_RESULT_SUM_SIZE (8) // 和结果大小
#define V_RESULT_AVG_SIZE (4) // 平均值大小
#define V_RESULT_MAX_SIZE (4) // 最大值大小
#define V_RESULT_MIN_SIZE (4) // 最小值大小

#define SET_BIT(var, pos) ((var) |= (1 << (pos)))    // 将指定比特位置1
#define CLEAR_BIT(var, pos) ((var) &= ~(1 << (pos))) // 将指定比特位清0
#define TOGGLE_BIT(var, pos) ((var) ^= (1 << (pos))) // 将指定比特位取反
#define CHECK_BIT(var, pos) ((var) & (1 << (pos)))   // 获取指定比特位对应的值(10进制)
#define GET_BIT(var, pos) (((var) >> (pos)) & 1)     // 获取指定比特位对应的值(2进制)

// 将双精度浮点数的低5位置0
#define SET_LOW_5_BITS_TO_ZERO(num) \
    (*((uint64_t *)&(num)) &= ~(0x1F))

#define FREE(ptr)          \
    do                     \
    {                      \
        if (ptr)           \
        {                  \
            free(ptr);     \
            ptr = nullptr; \
        }                  \
    } while (0)

// extern char root_dir[];
void gen_array(Reg32 *array, size_t array_size, Reg32 start, S32 interval);
void random64(Reg64 *data);
int32_t frexpf_ext(float value, int32_t *exp);
int64_t frexp_ext(double value, int32_t *exp);

void gen_mem_upasm_diff_buffer_lable(FILE *file, U32 size);
void gen_upinc_diff_buffer_lable(FILE *file);
void gen_upasm_head_code(FILE *file);
void gen_upasm_tail_code(FILE *file);
void gen_upasm_1_op_code(FILE *file, const char *instruction,
                         size_t opt1_size, size_t opt1_loop_cnt,
                         size_t dst_size, U8 idx = 1);
void gen_upasm_2_op_code(FILE *file, const char *instruction,
                         size_t opt1_size, size_t opt1_loop_cnt,
                         size_t opt2_size, size_t opt2_loop_cnt,
                         size_t dst_size, U8 idx = 1);
void gen_upasm_2_op_muls64_64_code(FILE *file, const char *instruction,
                                   size_t opt1_size, size_t a_size, size_t a_interval, U8 opt1_idx,
                                   size_t opt2_size, size_t b_size, size_t b_interval, U8 opt2_idx,
                                   size_t dst_size, U8 res_idx = 1);
void gen_upasm_2_op_atk_rls_code(FILE *file, const char *instruction, S32 reg_r38,
                                 size_t opt1_size, size_t opt1_loop_cnt,
                                 size_t opt2_size, size_t opt2_loop_cnt,
                                 size_t dst_size, U8 res_idx = 1);

void gen_upasm_2_op_init_dst_code(FILE *file, const char *instruction,
                                  size_t opt1_size, size_t opt1_loop_cnt,
                                  size_t opt2_size, size_t opt2_loop_cnt,
                                  size_t dst_size, size_t dst_loop_cnt, U8 idx = 1);
void gen_upasm_1_op_init_dst_code(FILE *file, const char *instruction,
                                  size_t opt1_size, size_t opt1_loop_cnt,
                                  size_t dst_size, size_t dst_loop_cnt, U8 idx = 1);
void gen_memupasm_input_data_lable(FILE *file, const char *instruction, U8 idx,
                                   const Reg32 *data, size_t data_size, size_t opt_size);
void gen_mem_upasm_result_data_lable(FILE *file, const char *instruction,
                                     const Reg32 *data, size_t size, U8 idx = 1);
void gen_upinc_input_data_lable(FILE *file, const char *instruction, U8 idx);
void gen_upinc_result_data_lable(FILE *file, const char *instruction, U8 idx = 1);
bool set_opt(void *opt, size_t opt_size, const Reg32 *data, size_t data_size, U32 index);
bool set_result(Reg32 *res, size_t max_res_size, size_t *index, const void *dst, size_t dst_size);
bool is_nan(U32 hex);
bool is_inf(U32 hex);
bool is_zero(U32 hex);
F32 hex_conv_f(U32 hex);
bool is_zero_ff(double ff);
bool is_zero_f(float f);
int32_t creat_u31_project(const char *project_name, U31CodeFile &u31_code_file);
void close_project(const U31CodeFile &u31_code_file);
const char *get_instruction_from_func(const char *str);
void gen_vector_upinc_label(FILE *file, const char *instruction, const char *label, int32_t case_num);
void gen_vector_upasm_main_code(FILE *file, const char *instruction, int32_t case_num, const VectorParam &vp);
void gen_vector_upasm_head_code(FILE *file, const char *instruction, int32_t case_num);
void gen_u31_code(const U31CodeFile &u31_code_file, const char *instruction, int32_t case_num, const VectorParam &vp);
bool handle_special_data_fmulc(int a, int b, int c, int d, int &real, int &imag);
bool handle_special_data_fmuladd(int a, int b, int c, int &sum);
bool handle_special_data_fmulsub(int a, int b, int c, int &diff);

template <typename T>
void vector_abs(T *data, size_t num)
{
    for (size_t i = 0; i < num; i++)
    {
        T tmp = *(data + i);
        if (std::numeric_limits<T>::min() == tmp)
            *(data + i) = std::numeric_limits<T>::max();
        else
            *(data + i) = std::abs(tmp);
    }
}

template <typename T1, typename T2>
T1 get_limits(T2 value)
{
    if (value < std::numeric_limits<T1>::min())
        return std::numeric_limits<T1>::min();
    else if (value > std::numeric_limits<T1>::max())
        return std::numeric_limits<T1>::max();
    else
        return static_cast<T1>(value);
}
template <typename T>
T u31_abs(T x)
{
    if (std::numeric_limits<T>::min() == x)
        return std::numeric_limits<T>::max();
    else
        return (x < 0 ? -x : x);
}
/**
 * 功能: 不同数据类型之间转换(模板函数)
 * 参数:
 *   - in_ptr: 输入数据指针
 *   - out_ptr: 输出数据指针
 *   - num: 数据个数
 * 返回值: 无
 */
/** u31不同数据类型之间转换小tips:
 * a.当输入类型和输出类型一致,直接连续复制;
 * b.当输入类型宽度小于输出类型,输入数据按类型连续复制,复制的数据放在输出的高位,低位补0,如:
 * 输入0x00018000,类型为16bit,复制2个数据,输出为32bit, 0x8000、0x0001 ===> 0x80000000、0x00010000;
 * c.当输入类型宽度大于输出类型,输入数据按类型连续复制,复制的数据截取高位,低位舍入(正数:大于等于0.5,进位;负数:小于-0.5,进位),溢出取极值,如:
 * 输入0x00018000和0xfffe8000,类型为32bit,复制2个数据,输出为16bit, 0x00018000、0xfffe8000 ===> 0x0002、0xfffe;
 * 输入0x00017fff和0xfffe8001,类型为32bit,复制2个数据,输出为16bit, 0x00017fff、0xfffe8001 ===> 0x0001、0xffff;
 * d.求和、最大值、最小值、均值都是基于输出数据来的
 */
template <typename T1, typename T2, typename = std::enable_if_t<!std::is_void<T1>::value && !std::is_void<T2>::value>>
void read_data(T1 *in_ptr, T2 *out_ptr, int32_t num)
{
    size_t T2_size = sizeof(T2) * 8;
    size_t T1_size = sizeof(T1) * 8;
    for (size_t i = 0; i < num; i++)
    {
        if (T1_size < T2_size)
        {
            *(T2 *)(out_ptr + i) = (*(T1 *)(in_ptr + i)) << (T2_size - T1_size);
        }
        else if (T1_size > T2_size)
        {
            *(T2 *)(out_ptr + i) = get_limits<T2>(round(*(T1 *)(in_ptr + i) / pow(2, T1_size - T2_size)));
        }
        else
        {
            *(T2 *)(out_ptr + i) = *(T1 *)(in_ptr + i);
        }
    }
}
/**
 * 功能: 设置指定范围bit的值
 * 参数:
 *   - value: 被设置的参数
 *   - start: bit位起点
 *   - end: bit位终点
 *   - bits: 值
 * 返回值: 设置是否成功 false:失败 true:成功
 */
template <typename T, typename = std::enable_if_t<!std::is_void<T>::value>>
bool set_bits(T &value, int32_t start, int32_t end, int32_t bits)
{
    if (start < 0 || end < 0 || start > end || end >= sizeof(T) * 8)
    {
        printf("rang error! start=%d, end=%d\n", start, end);
        return false;
    }

    // 创建掩码
    int mask = ((1 << (end - start + 1)) - 1) << start;

    // 清除原始值的指定范围的位
    value &= ~mask;

    // 设置新的位值
    value |= (bits << start) & mask;
    return true;
}

/**
 * 功能:生成向量指令的内存文件标签(模板函数)
 * file: 输出文件句柄
 * instruction: 指令名
 * label: 标签名
 * case_num: 测试用例编号
 * data: 数据源数组
 * data_size: 数据源数组大小
 */
template <typename T, typename = std::enable_if_t<!std::is_void<T>::value>>
void gen_vector_mem_upsam_label(FILE *file, const char *instruction, const char *label, int32_t case_num, const T *data, size_t data_size)
{
    if (!file)
    {
        printf("file is nulltpr!\n");
        return;
    }
    size_t T_size = sizeof(T);
    const char *word = (1 == T_size) ? "byte" : (2 == T_size) ? "short"
                                                              : "word";
    const char *format = (1 == T_size) ? "0x%02x " : (2 == T_size) ? "0x%04x "
                                                                   : "0x%08x ";
    if (!data)
        fprintf(file, "#export %s_case%d_%s\n%s_case%d_%s:", instruction, case_num, label, instruction, case_num, label);
    else
        fprintf(file, "#export %s_case%d_%s\n%s_case%d_%s:\n\t.%s ", instruction, case_num, label, instruction, case_num, label, word);
    for (size_t i = 0; i < data_size; i++)
    {
        fprintf(file, format, static_cast<T>(data[i]));
    }
    fprintf(file, "\n");
    fprintf(file, "\t.align 8\n");
}

/**
 * 功能:随机生成不同数据类型数组(模板函数)
 * array:输入数组
 * n:数组大小
 * upper:设置数组值的上限,默认为1,即[0,RAND_MAX]
 */
template <typename T>
void random_gen(T *array, size_t n, int32_t upper = RAND_MAX)
{
    // 获取当前时间点
    auto now = std::chrono::high_resolution_clock::now();

    // 转换为自纪元以来的纳秒数
    auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch());

    srand(duration.count()); // 用当前时间初始化随机数种子

    for (size_t i = 0; i < n; i++)
    {
        /**因为随机生成保证不了每次都生成边界值，特别是上限永远取不到,故强制设置两个边界值*/
        if (n - 1 == i)
            array[i] = static_cast<T>(upper);
        else if (0 == i)
            array[i] = static_cast<T>(0);
        else
        {
            srand(duration.count() + i); // 用当前时间初始化随机数种子
            array[i] = static_cast<T>(rand() % upper) + static_cast<T>(rand()) / static_cast<T>(RAND_MAX);
        }
    }

    return;
}

/**
 * 功能:打印不同数据类型数组(模板函数)
 * array:输入参数,数组地址
 * size:输入参数,数组大小
 * format:输入参数,打印格式化
 */
template <typename T>
void print_array(const T *array, size_t size, const char *format = "%d")
{
    for (size_t i = 0; i < size; ++i)
    {
        printf(format, array[i]);
        if (i < size - 1)
        {
            printf(", ");
        }
        else
        {
            printf("\n");
        }
    }
}

/**
 * 功能: c程序2个操作数的测试代码宏
 * mem_upasm_file: 生成的app_memory.upasm文件句柄
 * upinc_file:生成的app.upinc文件句柄
 * a: 操作数1的数据源数组
 * b: 目的数的数据源数组
 * opt1: 操作数1
 * dst: 目的数
 * instruction: 指令名
 */
#define GENERATE_1_OP_INIT_DST_CODE(mem_upasm_file, upinc_file, upasm_file, a, b, opt1, dst, instruction) \
    do                                                                                                    \
    {                                                                                                     \
        const char *functionName = __func__;                                                              \
        char arr[80] = {0};                                                                               \
        strncpy(arr, functionName + 5, sizeof(arr) - 1);                                                  \
        arr[sizeof(arr) - 1] = '\0';                                                                      \
        size_t a_size = sizeof(a) / sizeof(U32);                                                          \
        size_t b_size = sizeof(b) / sizeof(U32);                                                          \
        size_t opt1_size = sizeof(opt1) / sizeof(U32);                                                    \
        size_t dst_size = sizeof(dst) / sizeof(U32);                                                      \
        size_t res_size = a_size * b_size * dst_size;                                                     \
        Reg32 res[res_size] = {0};                                                                        \
        size_t index = 0;                                                                                 \
        for (size_t i = 0; i < a_size; i++)                                                               \
        {                                                                                                 \
            set_opt((void *)&opt1, opt1_size, a, a_size, i);                                              \
            for (size_t j = 0; j < b_size; j++)                                                           \
            {                                                                                             \
                set_opt((void *)&dst, dst_size, b, b_size, j);                                            \
                instruction(&dst, &opt1);                                                                 \
                set_result((Reg32 *)&res, res_size, &index, (void *)&dst, dst_size);                      \
            }                                                                                             \
        }                                                                                                 \
        gen_memupasm_input_data_lable(mem_upasm_file, arr, 1, a, a_size, opt1_size);                      \
        gen_memupasm_input_data_lable(mem_upasm_file, arr, 2, b, b_size, dst_size);                       \
        gen_mem_upasm_result_data_lable(mem_upasm_file, arr, res, res_size);                              \
        gen_upinc_input_data_lable(upinc_file, arr, 1);                                                   \
        gen_upinc_input_data_lable(upinc_file, arr, 2);                                                   \
        gen_upinc_result_data_lable(upinc_file, arr);                                                     \
        gen_upasm_1_op_init_dst_code(upasm_file, arr, opt1_size, a_size, dst_size, b_size);               \
    } while (0)

/**
 * 功能: c程序2个操作数的测试代码宏
 * mem_upasm_file: 生成的app_memory.upasm文件句柄
 * upinc_file:生成的app.upinc文件句柄
 * a: 操作数1的数据源数组
 * b: 操作数2的数据源数组
 * opt1: 操作数1
 * opt2: 操作数2
 * dst: 目的数
 * instruction: 指令名
 */
#define GENERATE_2_OP_INIT_DST_CODE(mem_upasm_file, upinc_file, upasm_file, a, b, c, opt1, opt2, dst, instruction) \
    do                                                                                                             \
    {                                                                                                              \
        const char *functionName = __func__;                                                                       \
        char arr[80] = {0};                                                                                        \
        strncpy(arr, functionName + 5, sizeof(arr) - 1);                                                           \
        arr[sizeof(arr) - 1] = '\0';                                                                               \
        size_t a_size = sizeof(a) / sizeof(U32);                                                                   \
        size_t b_size = sizeof(b) / sizeof(U32);                                                                   \
        size_t c_size = sizeof(c) / sizeof(U32);                                                                   \
        size_t opt1_size = sizeof(opt1) / sizeof(U32);                                                             \
        size_t opt2_size = sizeof(opt2) / sizeof(U32);                                                             \
        size_t dst_size = sizeof(dst) / sizeof(U32);                                                               \
        size_t res_size = a_size * b_size * c_size * dst_size;                                                     \
        Reg32 res[res_size] = {0};                                                                                 \
        size_t index = 0;                                                                                          \
        for (size_t i = 0; i < a_size; i++)                                                                        \
        {                                                                                                          \
            set_opt((void *)&opt1, opt1_size, a, a_size, i);                                                       \
            for (size_t j = 0; j < b_size; j++)                                                                    \
            {                                                                                                      \
                set_opt((void *)&opt2, opt2_size, b, b_size, j);                                                   \
                for (size_t k = 0; k < c_size; k++)                                                                \
                {                                                                                                  \
                    set_opt((void *)&dst, dst_size, c, c_size, k);                                                 \
                    instruction(&dst, &opt1, &opt2);                                                               \
                    set_result((Reg32 *)&res, res_size, &index, (void *)&dst, dst_size);                           \
                }                                                                                                  \
            }                                                                                                      \
        }                                                                                                          \
        gen_memupasm_input_data_lable(mem_upasm_file, arr, 1, a, a_size, opt1_size);                               \
        gen_memupasm_input_data_lable(mem_upasm_file, arr, 2, b, b_size, opt2_size);                               \
        gen_memupasm_input_data_lable(mem_upasm_file, arr, 3, c, c_size, dst_size);                                \
        gen_mem_upasm_result_data_lable(mem_upasm_file, arr, res, res_size);                                       \
        gen_upinc_input_data_lable(upinc_file, arr, 1);                                                            \
        gen_upinc_input_data_lable(upinc_file, arr, 2);                                                            \
        gen_upinc_input_data_lable(upinc_file, arr, 3);                                                            \
        gen_upinc_result_data_lable(upinc_file, arr);                                                              \
        gen_upasm_2_op_init_dst_code(upasm_file, arr, opt1_size, a_size, opt2_size, b_size, dst_size, c_size);     \
    } while (0)

/**
 * 功能: c程序2个操作数的测试代码宏
 * mem_upasm_file: 生成的app_memory.upasm文件句柄
 * upinc_file:生成的app.upinc文件句柄
 * a: 操作数1的数据源数组
 * b: 操作数2的数据源数组
 * opt1: 操作数1
 * opt2: 操作数2
 * dst: 目的数
 * instruction: 指令名
 */
#define GENERATE_2_OP_CODE(mem_upasm_file, upinc_file, upasm_file, a, b, opt1, opt2, dst, instruction) \
    do                                                                                                 \
    {                                                                                                  \
        const char *functionName = __func__;                                                           \
        char arr[80] = {0};                                                                            \
        strncpy(arr, functionName + 5, sizeof(arr) - 1);                                               \
        arr[sizeof(arr) - 1] = '\0';                                                                   \
        size_t a_size = sizeof(a) / sizeof(U32);                                                       \
        size_t b_size = sizeof(b) / sizeof(U32);                                                       \
        size_t opt1_size = sizeof(opt1) / sizeof(U32);                                                 \
        size_t opt2_size = sizeof(opt2) / sizeof(U32);                                                 \
        size_t dst_size = sizeof(dst) / sizeof(U32);                                                   \
        size_t res_size = a_size * b_size * dst_size;                                                  \
        Reg32 res[res_size] = {0};                                                                     \
        size_t index = 0;                                                                              \
        for (size_t i = 0; i < a_size; i++)                                                            \
        {                                                                                              \
            set_opt((void *)&opt1, opt1_size, a, a_size, i);                                           \
            for (size_t j = 0; j < b_size; j++)                                                        \
            {                                                                                          \
                memset((char *)&dst, 0x00, sizeof(dst));                                               \
                set_opt((void *)&opt2, opt2_size, b, b_size, j);                                       \
                instruction(&dst, &opt1, &opt2);                                                       \
                set_result((Reg32 *)&res, res_size, &index, (void *)&dst, dst_size);                   \
            }                                                                                          \
        }                                                                                              \
        gen_memupasm_input_data_lable(mem_upasm_file, arr, 1, a, a_size, opt1_size);                   \
        gen_memupasm_input_data_lable(mem_upasm_file, arr, 2, b, b_size, opt2_size);                   \
        gen_mem_upasm_result_data_lable(mem_upasm_file, arr, res, res_size);                           \
        gen_upinc_input_data_lable(upinc_file, arr, 1);                                                \
        gen_upinc_input_data_lable(upinc_file, arr, 2);                                                \
        gen_upinc_result_data_lable(upinc_file, arr);                                                  \
        gen_upasm_2_op_code(upasm_file, arr, opt1_size, a_size, opt2_size, b_size, dst_size);          \
    } while (0)

/**
 * 功能: c程序1个操作数的测试代码宏
 * mem_upasm_file: 生成的app_memory.upasm文件句柄
 * upinc_file:生成的app.upinc文件句柄
 * a: 操作数1的数据源数组
 * opt1: 操作数1
 * dst: 目的数
 * instruction: 指令名
 */
#define GENERATE_1_OP_CODE(mem_upasm_file, upinc_file, upasm_file, a, opt1, dst, instruction) \
    do                                                                                        \
    {                                                                                         \
        const char *functionName = __func__;                                                  \
        char arr[80] = {0};                                                                   \
        strncpy(arr, functionName + 5, sizeof(arr) - 1);                                      \
        arr[sizeof(arr) - 1] = '\0';                                                          \
        size_t a_size = sizeof(a) / sizeof(U32);                                              \
        size_t opt1_size = sizeof(opt1) / sizeof(U32);                                        \
        size_t dst_size = sizeof(dst) / sizeof(U32);                                          \
        size_t res_size = a_size * dst_size;                                                  \
        Reg32 res[res_size] = {0};                                                            \
        size_t index = 0;                                                                     \
        for (size_t i = 0; i < a_size; i++)                                                   \
        {                                                                                     \
            set_opt((void *)&opt1, opt1_size, a, a_size, i);                                  \
            memset((char *)&dst, 0x00, sizeof(dst));                                          \
            instruction(&dst, &opt1);                                                         \
            set_result((Reg32 *)&res, res_size, &index, (void *)&dst, dst_size);              \
        }                                                                                     \
        gen_memupasm_input_data_lable(mem_upasm_file, arr, 1, a, a_size, opt1_size);          \
        gen_mem_upasm_result_data_lable(mem_upasm_file, arr, res, res_size);                  \
        gen_upinc_input_data_lable(upinc_file, arr, 1);                                       \
        gen_upinc_result_data_lable(upinc_file, arr);                                         \
        gen_upasm_1_op_code(upasm_file, arr, opt1_size, a_size, dst_size);                    \
    } while (0)

#endif // ! __COMMON_H
